package protocol

import (
	"fmt"
	gos7 "github.com/robinson/gos7"
	"goAdapter/device/commInterface"
	"goAdapter/setting"
	"log"
	"os"
	"time"
)

const (
	SiemensS7RegTypeInput  int = iota //开关型
	SiemensS7RegTypeOutput            //单字节数值型
	SiemensS7RegTypeM                 //双字节数值型
	SiemensS7RegTypeDB                //四字节数值型
)

const (
	SiemensS7DateTypeBit0 int = iota //开关型
	SiemensS7DateTypeBit1
	SiemensS7DateTypeBit2
	SiemensS7DateTypeBit3
	SiemensS7DateTypeBit4
	SiemensS7DateTypeBit5
	SiemensS7DateTypeBit6
	SiemensS7DateTypeBit7
	SiemensS7DateTypeByte       //单字节数值型
	SiemensS7DateTypeWByte = 16 //双字节数值型
	SiemensS7DateTypeDByte = 32 //四字节数值型
)

type SiemensS7Template struct {
	Name      string                               //协议名
	rack      int                                  //机架
	slot      int                                  //槽号
	Sid       int                                  //从机地址
	UseComIf  bool                                 //是否使用标准通信接口
	comIf     commInterface.CommunicationInterface //通信接口
	isConnect bool
	handler   *gos7.TCPClientHandler
	client    gos7.Client
}

func (c *SiemensS7Template) Construct() {
	c.Name = "siemensS7"
	c.rack = 0
	c.slot = 0
	c.Sid = -1
	c.comIf = nil
	c.UseComIf = false
	c.isConnect = false
}

func (c *SiemensS7Template) Init(inf commInterface.CommunicationInterface) bool {
	c.comIf = inf

	if inf.GetType() != commInterface.CommTypeTcpClient {
		setting.ZAPS.Errorf("err comm type")
	}

	para := inf.GetParam().TcpCParam
	setting.ZAPS.Debugf("protocol [%s] ip=[%s]", c.Name, para.IP)

	addr := fmt.Sprintf("%s:%s", para.IP, para.Port)
	c.handler = gos7.NewTCPClientHandler(addr, c.rack, c.slot)

	c.handler.Timeout = 200 * time.Second
	c.handler.IdleTimeout = 200 * time.Second
	c.handler.Logger = log.New(os.Stdout, "tcp: ", log.LstdFlags)

	return true
}

func (c *SiemensS7Template) Connect() bool {
	// Connect manually so that multiple requests are handled in one connection session
	err := c.handler.Connect()
	if err != nil {
		setting.ZAPS.Errorf("client connect err")
		c.isConnect = false
		return false
	}

	c.client = gos7.NewClient(c.handler)

	c.isConnect = true
	//输出PLC运行状态
	fmt.Println(c.client.PLCGetStatus())

	return true
}

var siemensS7RegTypeMap = map[string]int{
	"I":  SiemensS7RegTypeInput,
	"O":  SiemensS7RegTypeOutput,
	"M":  SiemensS7RegTypeM,
	"DB": SiemensS7RegTypeDB,
}
var siemensS7DataTypeMap = map[string]int{
	"BIT0":  SiemensS7DateTypeBit0,
	"BIT1":  SiemensS7DateTypeBit1,
	"BIT2":  SiemensS7DateTypeBit2,
	"BIT3":  SiemensS7DateTypeBit3,
	"BIT4":  SiemensS7DateTypeBit4,
	"BIT5":  SiemensS7DateTypeBit5,
	"BIT6":  SiemensS7DateTypeBit6,
	"BIT7":  SiemensS7DateTypeBit7,
	"BYTE":  SiemensS7DateTypeByte,
	"INT16": SiemensS7DateTypeWByte,
	"INT32": SiemensS7DateTypeDByte,
}

func (c *SiemensS7Template) GetNode(node *NodeProperty) int {
	//setting.ZAPS.Debugf("%s get node %s", c.Name, node.ID)
	//先检查连接情况，如果没有连接，先尝试连接
	if !c.isConnect {
		ret := c.Connect()
		//如果还连接不成功，不再查询
		if !ret {
			return -1
		}
	}

	nodeRegType := siemensS7RegTypeMap[node.RegType]
	nodeDataType := siemensS7DataTypeMap[node.DataType]

	var helper gos7.Helper

	buffer := make([]byte, 255)

	//拼接成获取地址
	switch nodeRegType {
	case SiemensS7RegTypeInput:
		fallthrough
	case SiemensS7RegTypeOutput:
		fallthrough
	case SiemensS7RegTypeM:
		var addrString = fmt.Sprintf("%s%d", node.RegType, node.RegAddr)
		c.client.Read(addrString, buffer)
		switch nodeDataType {
		case SiemensS7DateTypeBit0:
			fallthrough
		case SiemensS7DateTypeBit1:
			fallthrough
		case SiemensS7DateTypeBit2:
			fallthrough
		case SiemensS7DateTypeBit3:
			fallthrough
		case SiemensS7DateTypeBit4:
			fallthrough
		case SiemensS7DateTypeBit5:
			fallthrough
		case SiemensS7DateTypeBit6:
			fallthrough
		case SiemensS7DateTypeBit7:
			node.Value = helper.GetBoolAt(buffer[0], uint(nodeDataType))
		case SiemensS7DateTypeByte:
			var bValue int8
			helper.GetValueAt(buffer, 0, &bValue)
			node.Value = bValue
		case SiemensS7DateTypeWByte:
			var wValue int16
			helper.GetValueAt(buffer, 0, &wValue)
			node.Value = wValue
		case SiemensS7DateTypeDByte:
			var dValue int32
			helper.GetValueAt(buffer, 0, &dValue)
			node.Value = dValue
		}
	case SiemensS7RegTypeDB:

	}
	return 0
}

func (c *SiemensS7Template) SetNode(node *NodeProperty) int {
	setting.ZAPS.Debugf("%s set node %s", c.Name, node.ID)
	if !c.isConnect {
		ret := c.Connect()
		//如果还连接不成功，不再查询
		if !ret {
			return -1
		}
	}

	nodeRegType := siemensS7RegTypeMap[node.RegType]
	nodeDataType := siemensS7DataTypeMap[node.DataType]

	sendByte := []byte(fmt.Sprintf("%v", node.SetValue.(interface{})))

	//拼接成获取地址
	switch nodeRegType {
	case SiemensS7RegTypeInput:
		switch nodeDataType {
		case SiemensS7DateTypeByte:
			c.client.AGWriteEB(node.RegAddr, 1, sendByte)
		case SiemensS7DateTypeWByte:
			c.client.AGWriteEB(node.RegAddr, 2, sendByte)
		case SiemensS7DateTypeDByte:
			c.client.AGWriteEB(node.RegAddr, 4, sendByte)
		}
	case SiemensS7RegTypeOutput:
		switch nodeDataType {
		case SiemensS7DateTypeByte:
			c.client.AGWriteAB(node.RegAddr, 1, sendByte)
		case SiemensS7DateTypeWByte:
			c.client.AGWriteAB(node.RegAddr, 2, sendByte)
		case SiemensS7DateTypeDByte:
			c.client.AGWriteAB(node.RegAddr, 4, sendByte)
		}
	case SiemensS7RegTypeM:
		switch nodeDataType {
		case SiemensS7DateTypeByte:
			c.client.AGWriteMB(node.RegAddr, 1, sendByte)
		case SiemensS7DateTypeWByte:
			c.client.AGWriteAB(node.RegAddr, 2, sendByte)
		case SiemensS7DateTypeDByte:
			c.client.AGWriteAB(node.RegAddr, 4, sendByte)
		}
	case SiemensS7RegTypeDB:
		// 目前未实现DB
	}

	return 0
}

func (c *SiemensS7Template) GetName() string {
	return c.Name
}

func (c *SiemensS7Template) GetUseComIf() bool {
	return c.UseComIf
}
func (c *SiemensS7Template) GetSid() int {
	return c.Sid
}

func (c *SiemensS7Template) SetSid(id int) {
	c.Sid = id
}

func (c *SiemensS7Template) GetComInf() commInterface.CommunicationInterface {
	return c.comIf
}
